﻿using System;
using System.Collections.Generic;
using System.Text;
using Barbar.UrlGuard.Dal.Persist;
using Barbar.UrlGuard.Dal;
using System.Threading;
using System.Net;
using System.Collections.ObjectModel;

namespace Barbar.UrlGuard.BL.Concrete {
  public class TickService : ITickService {
    private IDataContextHolder m_DataContextHolder;
    private IWebRequestUtility m_WebRequestUtility;
    
    private class TickThreadParameter {
      private ITickCallback m_TickCallback;
      private List<MonitoringEntry> m_Entries;
      private int m_SecondsToTimeout;

      public ITickCallback TickCallback {
        get { return m_TickCallback; }
      }

      public ReadOnlyCollection<MonitoringEntry> Entries {
        get { return new ReadOnlyCollection<MonitoringEntry>(m_Entries); }
      }

      public int SecondsToTimeout {
        get { return m_SecondsToTimeout; }
      }

      public TickThreadParameter(ITickCallback tickCallback, IEnumerable<MonitoringEntry> entries, int secondsToTimeout) {
        if (tickCallback == null)
          throw new ArgumentNullException("tickCallback");

        if (entries == null)
          throw new ArgumentNullException("entries");

        m_TickCallback = tickCallback;
        m_Entries = new List<MonitoringEntry>(entries);
        m_SecondsToTimeout = secondsToTimeout;
      }
    }

    private class CheckThreadParameter {
      private MonitoringEntry m_Entry;

      public MonitoringEntry Entry {
        get { return m_Entry; }
      }

      public CheckThreadParameter(MonitoringEntry entry) {
        if (entry == null)
          throw new ArgumentNullException("entry");

        m_Entry = entry;
      }
    }

    public TickService(IDataContextHolder dataContextHolder, IWebRequestUtility webRequestUtility) {
      if (dataContextHolder == null)
        throw new ArgumentNullException("dataContextHolder");

      if (webRequestUtility == null)
        throw new ArgumentNullException("webRequestUtility");

      m_DataContextHolder = dataContextHolder;
      m_WebRequestUtility = webRequestUtility;
    }

    private void TickStart(object paramObject) {
      TickThreadParameter parameter = paramObject as TickThreadParameter;

      DateTime now = DateTime.Now;
      List<MonitoringEntry> synchronizedEntries = new List<MonitoringEntry>();
      List<ManualResetEvent> resetEvents = new List<ManualResetEvent>();
      foreach (MonitoringEntry entry in parameter.Entries) {
        if (!entry.LastCheckDate.HasValue || entry.LastCheckDate.Value.Add(new TimeSpan(0, entry.Interval, 0)) <= now) {
          synchronizedEntries.Add(entry);
          entry.LastCheckDate = now;
          ManualResetEvent resetEvent = new ManualResetEvent(false);
          resetEvents.Add(resetEvent);
          ThreadPool.QueueUserWorkItem(CheckEntry, new object[] { entry, parameter.SecondsToTimeout, resetEvent });
        }
      }
      if (resetEvents.Count > 0)
        WaitHandle.WaitAll(resetEvents.ToArray());

      parameter.TickCallback.TickFinished(synchronizedEntries);
    }

    public void ProcessTick(ITickCallback callback) {
      List<MonitoringEntry> clonedEntries = new List<MonitoringEntry>();
      foreach (MonitoringEntry entry in m_DataContextHolder.Context.Entries) {
        clonedEntries.Add((MonitoringEntry)entry.Clone());
      }

      Thread backgroundWorker = new Thread(TickStart);
      backgroundWorker.Start(new TickThreadParameter(callback, clonedEntries, m_DataContextHolder.Context.SecondsToTimeout));
    }

    private void CheckEntry(object stateBag) {
      if (stateBag == null)
        throw new ArgumentNullException("stateBag");

      object[] states = (object[])stateBag;

      MonitoringEntry entry = (MonitoringEntry)states[0];
      int secondsToTimeout = (int)states[1];
      ManualResetEvent resetEvent = (ManualResetEvent)states[2];

      m_WebRequestUtility.CheckEntry(entry, secondsToTimeout * 1000);
      resetEvent.Set();
    }
  }
}
